/*
This is part of Screenbar, which is based in part on the Litestep
shell source code.

Copyright (C) 2008-2009 Jim Babcock
Copyright (C) 1997-2009 The LiteStep Development Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "stdafx.h"
#include <winuser.h>

BangCommandManager *bangManager = NULL;

void BangCommandManager::initBangs()
{
	// All commands exist with both 'vwm' prefix (for compatibility with hotkeys
	// that people might put in their shared-config files, which other VWMs will
	// also respect) and 'sb' prefix (for hotkeys that other VWMs can't't or
	// shouldn't recognize, and for consistency with variables).
	for(unsigned ii=0; ii<bangs.size(); ii++)
	{
		string vwmPrefixName = string("!vwm") + bangs[ii].name;
		string sbPrefixName = string("!sb") + bangs[ii].name;
		
		if(settings->useVWMPrefixedBangs)
			AddBangCommand(vwmPrefixName.c_str(), bangs[ii].func);
		AddBangCommand(sbPrefixName.c_str(), bangs[ii].func);
	}
}

void BangCommandManager::cleanupBangs()
{
	for(unsigned ii=0; ii<bangs.size(); ii++)
	{
		string vwmPrefixName = string("!vwm") + bangs[ii].name;
		string sbPrefixName = string("!sb") + bangs[ii].name;
		if(settings->useVWMPrefixedBangs)
			RemoveBangCommand(vwmPrefixName.c_str());
		RemoveBangCommand(sbPrefixName.c_str());
	}
}

void BangCommandManager::addBang(const char *name, BangCommandFunc func)
{
	BangCommandInfo cmd = {name, func};
	bangs.push_back(cmd);
}

BangCommandRegistration::BangCommandRegistration(BangCommandFunc func, const char *name)
{
	if(!bangManager)
		bangManager = new BangCommandManager();
	bangManager->addBang(name, func);
}


//////////////////////////////////////////////////////////////////////////////

BangCommand(Create)
{
	VirtualDesktop *newDesk = vwm->createDesktop(vwm->numDesktops());
	
	WindowData *foreground = windowTracker->getForegroundWindow();
	if(!foreground) {
		windowTracker->raiseLocalForeground();
		foreground = windowTracker->getForegroundWindow();
	}
	
	if(foreground)
		vwm->moveApp(foreground, newDesk);
}

BangCommand(Destroy)
{
	VirtualDesktop *desk = findSingleDesk(args);
	if(desk)
		vwm->destroyDesktop(desk);
}

BangCommand(Merge)
{
	VirtualDesktop *currentDesk = monitors->findMonitor(getCursorPos())->getDesk();
	VirtualDesktop *desk = findSingleDesk(args);
	if(desk)
		vwm->mergeDesk(currentDesk, desk);
}

BangCommand(Gather)
{
	vwm->gather();
}

BangCommand(Separate)
{
	VirtualDesktop *desk = findSingleDesk(args);
	if(desk)
		vwm->separateDesk(desk);
}

BangCommand(SeparateAll)
{
	vwm->separateDesk(NULL);
}

BangCommand(MoveDesk)
{
	VirtualDesktop *currentDesk = monitors->getPrimaryMonitor()->getDesk();
	VirtualDesktop *swapDesk = findSingleDesk(args);
	if(currentDesk && swapDesk)
		vwm->moveDesk(currentDesk->getIndex(), swapDesk->getIndex());
}

//////////////////////////////////////////////////////////////////////////////

BangCommand(Desk)
{
	char arg1[MAX_LINE_LENGTH], arg2[MAX_LINE_LENGTH];
	char *tokens[2] = {arg1, arg2};
	int numArgs = LCTokenize(args, tokens, 2, NULL);
	
	VirtualDesktop *desk;
	Monitor *monitor = NULL;
	
	desk = findSingleDesk(arg1);
	if(numArgs>1)
		monitor = findSingleMonitor(arg2);
	
	if(desk)
		vwm->switchDesk(desk, monitor);
}

BangCommand(Next)
{
	VirtualDesktop *desk = findSingleDesk("next");
	if(desk)
		vwm->switchDesk(desk);
}

BangCommand(Prev)
{
	VirtualDesktop *desk = findSingleDesk("prev");
	if(desk)
		vwm->switchDesk(desk);
}

BangCommand(Other)
{
	VirtualDesktop *desk = findSingleDesk("other");
	if(desk)
		vwm->switchDesk(desk);
}

BangAlias(Next, Right)
BangAlias(Next, Down)
BangAlias(Prev, Left)
BangAlias(Prev, Up)

//////////////////////////////////////////////////////////////////////////////

BangCommand(Toggle)
{
	set<ScreenbarPanel*> panels = findPanels(args);
	for(set<ScreenbarPanel*>::iterator ii=panels.begin(); ii!=panels.end(); ii++)
		(*ii)->setVisible(!(*ii)->getVisible());
}

BangCommand(Show)
{
	set<ScreenbarPanel*> panels = findPanels(args);
	for(set<ScreenbarPanel*>::iterator ii=panels.begin(); ii!=panels.end(); ii++)
		(*ii)->setVisible(true);
}

BangCommand(Hide)
{
	set<ScreenbarPanel*> panels = findPanels(args);
	for(set<ScreenbarPanel*>::iterator ii=panels.begin(); ii!=panels.end(); ii++)
		(*ii)->setVisible(false);
}

BangCommand(Open)
{
	char deskName[MAX_LINE_LENGTH], cmd[MAX_LINE_LENGTH];
	char *tokens[1] = {deskName};

	if (LCTokenize(args, tokens, 1, cmd))
	{
		VirtualDesktop *desk = findSingleDesk(deskName);
		if(desk) vwm->switchDesk(desk);
		LSExecute(NULL, cmd, SW_SHOWNORMAL);
	}
}

BangCommand(MoveApp)
{
	char arg1[MAX_LINE_LENGTH], arg2[MAX_LINE_LENGTH];
	char *tokens[2] = {arg1, arg2};
	int numArgs = LCTokenize(args, tokens, 2, NULL);
	
	char *deskName, *taskName;
	if(!numArgs) {
		taskName = NULL;
		deskName = "other";
	} else if(numArgs==1) {
		taskName = NULL;
		deskName = arg1;
	} else {
		taskName = arg1;
		deskName = arg2;
	}
	
	VirtualDesktop *desk = findSingleDesk(deskName);
	
	WindowData *window;
	if(taskName)
		window = findSingleWindow(taskName);
	else
		window = windowTracker->getForegroundWindow();
	
	if(desk && window)
		vwm->moveApp(window, desk);
}

BangCommand(OntopToggle)
{
	set<ScreenbarPanel*> panels = findPanels(args);
	for(set<ScreenbarPanel*>::iterator ii=panels.begin(); ii!=panels.end(); ii++)
		(*ii)->setAlwaysOnTop(!(*ii)->getAlwaysOnTop());
}

BangCommand(RaiseWindow)
{
	WindowData *window = findSingleWindow(args);
	if(window) {
		if(!window->desk->isVisible() && settings->switchOnFocus)
			vwm->switchDesk(window->desk);
		vwm->raiseWindow(window);
	}
}

BangCommand(MinimizeWindow)
{
	WindowData *window = findSingleWindow(args);
	if(window)
		window->minimize();
}

BangCommand(MaximizeWindow)
{
	WindowData *window = findSingleWindow(args);
	if(window) {
		if(IsZoomed(window->handle))
			window->restore();
		else
			window->maximize();
	}
}

BangCommand(MoveWindow)
{
	WindowData *window = findSingleWindow(args);
	if(window) {
		SendMessage(window->handle, WM_SYSCOMMAND, SC_MOVE, 0);
	}
}

BangCommand(ResizeWindow)
{
	WindowData *window = findSingleWindow(args);
	if(window) {
		SendMessage(window->handle, WM_SYSCOMMAND, SC_SIZE, 0);
	}
}

BangCommand(WindowContextMenu)
{
	WindowData *window = findSingleWindow(args);
	if(window) {
		Point cursorPos = getCursorPos();
		vwm->raiseWindow(window);
		Sleep(100);
		PostMessage(window->handle, 0x313, 0, MAKELONG(cursorPos.x, cursorPos.y));
	}
}

BangCommand(CloseWindow)
{
	WindowData *window = findSingleWindow(args);
	if(window) {
		PostMessage(window->handle, WM_CLOSE, 0, 0);
	}
}

// Usage: !sbReorderTask - Move dragged task to dragged position
//        !sbReorderTask [pos] - Move focused task to named position
//        !sbReorderTask [task] [pos]
// [pos] is one of:
//     hovered
//     first
//     last
BangCommand(ReorderTask)
{
	vector<string> argv = separateArgs(args);
	
	WindowData *taskWindow = NULL;
	TaskData *positionRel = NULL;
	bool positionAfter = true;
	bool reposition;
	
	if(argv.size() == 0) {
		taskWindow = findSingleWindow("dragged/focused");
		reposition = parseTaskPosition(&positionRel, &positionAfter, "hovered");
	} else if(argv.size() == 1) {	
		taskWindow = findSingleWindow("dragged/focused");
		reposition = parseTaskPosition(&positionRel, &positionAfter, argv[0]);
	} else if(argv.size() == 2) {
		taskWindow = findSingleWindow(argv[0]);
		reposition = parseTaskPosition(&positionRel, &positionAfter, argv[1]);
	}
	
	if(!reposition)
		return;
	if(!taskWindow || !positionRel || taskWindow->task==positionRel)
		return;
	
	if(positionAfter)
		windowTracker->reorderTask(taskWindow->task, positionRel, NULL);
	else
		windowTracker->reorderTask(taskWindow->task, NULL, positionRel);
	
	updateLayouts();
}

BangCommand(MoveAndReorder)
{
	vector<string> argv = separateArgs(args);
	
	WindowData *taskWindow = NULL;
	TaskData *positionRel = NULL;
	bool positionAfter = true;
	bool reposition;
	
	taskWindow = findSingleWindow("dragged/focused");
	reposition = parseTaskPosition(&positionRel, &positionAfter, "hovered");
	
	if(!reposition)
		return;
	if(!taskWindow || !positionRel || taskWindow->task==positionRel)
		return;
	
	if(positionAfter)
		windowTracker->reorderTask(taskWindow->task, positionRel, NULL);
	else
		windowTracker->reorderTask(taskWindow->task, NULL, positionRel);
	
	VirtualDesktop *desk = findSingleDesk("hovered");
	if(!desk) return;
	
	vwm->moveApp(taskWindow, desk);
	
	updateLayouts();
}
